# -*-coding:utf-8 -*-
"""
# File       : main_driver.py
# Time       ：2022/3/21 11:05
# Author     ：Coco
# version    ：python 3.7
"""
import json
import os.path
import pathlib
import sys

import configparser
from tkinter.messagebox import NO
from PyQt5.QtCore import QSize, pyqtSignal, QPoint, QTimer, QDate, QThread, Qt, QVariant
from PyQt5.QtGui import QIcon, QBrush, QColor
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QListWidgetItem, QHBoxLayout, QLabel, QPushButton, \
    QComboBox, QDateEdit, QDialog, QVBoxLayout, QFileDialog, QRadioButton, QLineEdit, QCheckBox
from gui.custom_widget.DCPDialog import DCPDialog
from gui.main_gui import Gui
from utils.read_file import get_all_file, handle_file
from utils.save_setting_2_ini import save_ini_info, save_data2_2_ini
from gui import constant
from gui.custom_widget.CommonDialog import CommonDialog
from gui.custom_widget.manual_dialog import ManualDialog
from collections import namedtuple
from utils.read_ini_2_dict import read_ini_info_2_dict
from utils.parse_txt_util import parse_txt_to_dict
from my_thread.mmThread import MeasureThread
from my_thread.measure_task import MeasureTask
from my_thread.manual_thread import ManualThread
from my_thread.my_thread_pool import MyThreadPool
from my_thread.probe_station_thread import ProbeStationThread
from my_thread.probe_station_task import ProbeStationTask
from my_thread.link_calibration_thread import LinkCalibrationThread
from ATS.proberdriver.cascade_prober import CascadeProber
from ATS.datamanager import data_transfer_station as DataTransferStation
from gui.driver.result_action import ResultAction
from utils.log_util import loggings
from utils.save_set_info_2_yaml import save_set_info_2_yaml
from gui.driver.var_manager import VarManager
from utils.create_folder_util import create_folder
import copy
from utils.save_data_2_file import save_data_2_file
from gui.data_analysis.data_analysis_view import DataAnalysisView
from gui.stability_repeatability_test.stability_repeatability_view import StabilityRepeatabilityView
from gui.driver.map_action import MapAction
from database.db_interface import insert_wafer
from configparser import ConfigParser
from concurrent.futures import ThreadPoolExecutor, as_completed
from my_thread.check_equip_comm_thread import CheckEquipCommTask
from my_thread.init_probe_task import InitProbeTask
from utils.get_equipment_list import get_equipment_address
from utils.base64salt_util import base_decode
from utils.check_time import check_regtime, get_software_reference
from gui.custom_widget.infoDialog import auto_close_dialog, show_info, show_critical, show_ask


class Driver(Gui):
    stop_signal = pyqtSignal()
    pause_signal = pyqtSignal()
    resume_signal = pyqtSignal()

    def __init__(self):
        super(Driver, self).__init__()
        self.link_cal_thread = None
        self.wafer_subdie_count = None
        self.wafer_die_count = None
        self.flush_data_timer = QTimer()
        self.check_task = None
        self.check_equipment_status_timer = QTimer()
        self.init_probe_timer = QTimer()
        self.check_lic_timer = QTimer()
        self.dmo = None
        self.var_manage = VarManager()
        self.thread_pool = []
        self.init_DataManagerObj()
        self.windows = []
        self.item_list = []
        self.dict_setting = {}
        self.probe_task = None
        self.measure_task = None
        self.probe_order_list = None
        self.die_x = None
        self.die_y = None
        self.map = None
        self.subdie_name = None
        self.probe_thread = None
        self.measure_thread = None
        self.subdie_list = None
        self.init_probe_thread = None
        self.init_probe_task = None
        self.productType = None
        self.cp = None
        self.result_window = None
        self.dcp_list = None
        self.result_dict = None
        self.manual_dialog = None
        self.check_thread = None
        self.stability_repeatability_view = None
        self.data_view = None
        self.dia_dcp = None
        self._bind_actions()
        self.all_sub_die_info_dict = {}
        self._init_ui()  # 初始化dcp 窗口
        self.init_check_lic_timer()

    def init_check_lic_timer(self):
        # 定义时间超时连接
        self.check_lic_timer.singleShot(2000, self.check_lic)
        # 启动任务
        self.check_lic_timer.start()

    def check_lic(self):
        boolean = self.check_lisence()
        if boolean:
            self.check_equipment_status()
            self.tb_start.setEnabled(True)
        else:
            self.set_all_tb_status(False)
            show_critical(self, "请联系供应商！")

    def set_all_tb_status(self, statu):
        self.setEnabled(statu)

    @staticmethod
    def check_lisence():
        try:
            reference = get_software_reference()
            if reference:
                handle_ref = base_decode(reference)
                boolean = check_regtime(int(handle_ref))
                loggings.info("boolean," + str(boolean))
                if boolean:
                    return boolean
                else:
                    return False
            else:
                return False
        except Exception as e:
            loggings.error("check_lisence," + str(e))
            return False

    def show_probe_init_error(self, error_msg):
        show_critical(self, error_msg)

    def clear_statusbar_widget(self):
        self.statusbar.clearMessage()
        status_widgets = self.statusbar.children()
        for widget in status_widgets:
            if widget.objectName() == "lb_status_red" or widget.objectName() == "lb_status_green":
                widget.hide()
                widget.deleteLater()

    def check_equipment_status(self):
        self.tb_flush_equipment.setEnabled(False)
        self.clear_statusbar_widget()
        equip_list = get_equipment_address()
        self.check_task = CheckEquipCommTask(equip_list)
        self.check_thread = QThread()
        self.check_task.moveToThread(self.check_thread)
        self.check_task.result_signal.connect(self.show_equipment_status)
        self.check_task.finished_signal.connect(self.check_finished)
        self.check_thread.start()
        self.check_equipment_status_timer.singleShot(1000, self.check_task.run)
        # 启动任务
        self.check_equipment_status_timer.start()

    def check_finished(self):
        if self.check_thread and self.check_thread.isRunning():
            self.check_thread.quit()
            self.check_thread.wait()
        self.tb_flush_equipment.setEnabled(True)

    def show_equipment_status(self, value):
        if value:
            if list(value.values())[0] == "success":
                lb_status = QLabel()
                lb_status.setObjectName("lb_status_green")
                message = f"{list(value.keys())[0]}连接成功"
            else:
                lb_status = QLabel()
                lb_status.setObjectName("lb_status_red")
                loggings.info(f"{list(value.keys())[0]}连接失败")
                message = f"{list(value.keys())[0]}连接失败"
            self.statusbar.addWidget(lb_status)
            auto_close_dialog(self, message)

    def flush_data(self):
        self.flush_data_timer.timeout.connect(self.flush_data_task)
        self.flush_data_timer.start(1000)

    def init_probe(self, wafer_die_count, subdie_label_list):
        self.wafer_die_count = int(wafer_die_count)
        self.wafer_subdie_count = len(subdie_label_list)
        self.get_test_info()

    def quit_init_thread(self):
        if self.init_probe_thread and self.init_probe_thread.isRunning():
            self.init_probe_thread.quit()
            self.init_probe_thread.wait()
            self.init_probe_thread = None
            self.init_probe_task = None

    def _init_ui(self):
        self.set_start_pause_continue_stop_status(False)
        self.dia_dcp = DCPDialog()
        self.dia_dcp.send_signal.connect(self.update_dcp_info)

    def set_start_pause_continue_stop_status(self, status):
        self.tb_start.setEnabled(1 - status)
        self.tb_pause.setEnabled(status)
        self.tb_continue.setEnabled(status)
        self.tb_stop.setEnabled(status)

    def init_DataManagerObj(self):
        self.dmo = DataTransferStation.get_DTS()

    def _bind_actions(self):
        self.tb_pair_smu_dcp.clicked.connect(self._show_dcp_wd)
        self.tb_save_config.clicked.connect(self.save_file)
        self.tb_read_config.clicked.connect(self.read_config)
        self.tb_start.clicked.connect(self.start_measure)
        self.tb_stop.clicked.connect(self.stop_measure)
        self.tb_pause.clicked.connect(self.pause_measure)
        self.tb_continue.clicked.connect(self.continue_measure)
        self.tb_import_subdie_file.clicked.connect(self.import_subdie_file)
        self.pb_spp_proj.clicked.connect(lambda: self.open_folder_file_path(1))
        self.pb_map_path.clicked.connect(lambda: self.open_folder_file_path(2))
        self.pb_file_folder.clicked.connect(lambda: self.open_folder_file_path(3))
        self.pb_lc_test.clicked.connect(self.link_calibration_test)
        self.tb_stop_sip_tools.clicked.connect(self.stop_sip_tools)
        self.tb_manual.clicked.connect(self.manual_test)
        self.tb_statis.clicked.connect(self.show_data_statis_view)
        self.tb_wafer.clicked.connect(self.show_wafer_map_view)
        self.tb_stability.clicked.connect(self.show_stability_view)
        self.tb_delete.clicked.connect(self.delete_test_seq)
        self.btn_add_item.clicked.connect(self.add_list_item)
        self.btn_delete_item.clicked.connect(self.delete_list_item)
        self.btn_probe_ensure.clicked.connect(self.show_ensure_info)
        self.tb_flush_equipment.clicked.connect(self.check_equipment_status)

    def show_wafer_map_view(self):
        try:
            self.cp = CascadeProber("127.0.0.1")
            result = self.cp.read_map_position()
            wafer_die_count = int(self.cp.get_wafer_info().DiesCount)
            subdie_list = []
            for i in range(result.LastSiteIndex):
                if self.cp.get_subdie_status(i) == "E":
                    subdie_list.append(i)
            if self.map is None:
                self.map = MapAction()
            self.map._refresh_ui_display(wafer_die_count, subdie_list)
            self.map.test_ingore_list.connect(self.get_test_ingore_list)
            self.map.movePos.connect(self.move_current_pos)
            self.map.show()
            self.windows.append(self.map)
        except Exception as e:
            loggings.error("show_wafer_map_view" + str(e))
            show_critical(self, "Velox 不在线！")

    def move_current_pos(self, pos):
        self.cp.step_to_die(pos[0], pos[1])

    def get_test_ingore_list(self, test_list,ingore_list):
        print("test_list,ingore_list", test_list,ingore_list)

    def show_stability_view(self):
        if self.stability_repeatability_view is None:
            self.stability_repeatability_view = StabilityRepeatabilityView()
        self.stability_repeatability_view.set_dcp_list(self.dcp_list)
        self.stability_repeatability_view.show()

    def delete_test_seq(self):
        if len(self.dict_setting):
            for value in self.dict_setting.values():
                if self.lw_test_area.item(value.row_num):
                    self.lw_test_area.item(value.row_num).setCheckState(Qt.Unchecked)
                    self.lw_test_area.item(value.row_num).setData(Qt.CheckStateRole, QVariant())
            self.dict_setting.clear()
            if len(self.dict_setting) == 0:
                show_info(self, "测试序列已清空！")
        else:
            show_critical(self, "测试序列不需要清理！")

    def show_ensure_info(self):
        probe_first = self.cb_probe_first.currentText()
        probe_second = self.cb_probe_second.currentText()
        probe_third = self.cb_probe_third.currentText()
        probe_four = self.cb_probe_four.currentText()
        probe_sequence = [probe_first, probe_second, probe_third, probe_four]
        handle_seq = []
        for probe in probe_sequence:
            if probe == "H1":
                probe = "W"
            elif probe == "H2":
                probe = "E"
            handle_seq.append(probe)

        if len(handle_seq) == 4:
            result = show_ask(self, "是否确认探针移动顺序的设置?")
            if result == QMessageBox.Yes:
                self.probe_order_list = handle_seq
        else:
            show_critical(self, "探针的编号不能相同！")

    def show_data_statis_view(self):
        if self.data_view is None:
            self.data_view = DataAnalysisView()
        self.data_view.show()
        self.windows.append(self.data_view)

    def manual_test(self):
        if self.manual_dialog is None:
            self.manual_dialog = ManualDialog()
            self.manual_dialog.send_signal.connect(self.manual_test_loss_gc)
        self.manual_dialog.exec()

    def manual_test_loss_gc(self, value):
        if len(value) == 3:
            self.die_x = value[0]
            self.die_y = value[1]
            self.subdie_name = value[2]
            self.var_manage.diex = self.die_x
            self.var_manage.diey = self.die_y
            self.var_manage.subdie_name = self.subdie_name

    def show_manual_result(self, value):
        if value:
            show_info(self, "测试完成！")
        else:
            show_critical(self, "测试失败！")

    def stop_sip_tools(self):
        try:
            cp = CascadeProber()
            if cp:
                cp.stop_sip_tools()
                show_info(self, "Siph_Tools的Slave模式停止！")
        except Exception as e:
            loggings.error("stop_sip_tools_error " + str(e))
            show_critical(self, "探针台不在线,请检查控制电脑与探针台电脑之间的连接！")

    def link_calibration_test(self):
        try:
            loss_setting = self.get_loss_setting()
            if loss_setting:
                save_data2_2_ini(constant.GUI_SETTING, "link_loss", loss_setting._asdict())
                self.link_cal_thread = LinkCalibrationThread(loss_setting)
                self.link_cal_thread.signal.result_state.connect(self.show_result)
                self.pool.start(self.link_cal_thread)
            else:
                show_critical(self, "链路损耗中的设置不能为空!")
        except Exception as e:
            loggings.error("执行链路校准时出现异常 " + str(e))
            show_critical(self, "执行链路校准时出现异常")

    def show_result(self, value):
        if value:
            show_info(self, "链路校准测试完成！")
        else:
            show_critical(self, "链路校准测试失败！")

    def get_test_result(self):
        test_result_pair = namedtuple("test_result", "spp_path,map_path,storage_path")
        spp_path = self.le_spp_proj.text()
        map_path = self.le_map_path.text()
        storage_path = self.le_file_folder.text()
        return test_result_pair(spp_path, map_path, storage_path)

    def get_test_info(self):
        operator = self.le_operator.text()
        self.machine_Id = self.le_machine.text()
        date = self.de_date.text()
        self.batch_number = self.le_batch_num.text()
        self.waferId = self.le_wafer_id.text()
        self.productType = self.le_product_type.text()
        self.save_folder = self.le_file_folder.text()
        if all([operator, self.machine_Id, date, self.batch_number, self.waferId, self.productType, self.save_folder]):
            path_file = pathlib.Path(
                f"{self.save_folder}/{operator}/{self.machine_Id}/{date.replace('-', '')}/{self.batch_number}/{self.waferId}/{self.productType}")
            create_folder(path_file)
            if path_file and self.waferId:
                self.var_manage.save_folder = path_file
                self.var_manage.wafer_name = self.waferId
            self.insert_wafer()
        else:
            show_critical(self, "operator,machineID,date等不能为空！")

    def import_subdie_file(self):
        try:
            self.project_meta_list = get_all_file(file_path=constant.PLANS_FILE)
            self.project_list = handle_file(self.project_meta_list)
            file_name, _ = QFileDialog.getOpenFileName(self, "导入文件", "./")
            if file_name:
                if pathlib.Path(file_name).exists():
                    self.result_dict = parse_txt_to_dict(file_name)
                    self.subdie_list = self.result_dict.keys()
                    self.dialog_manager_dict = {}
            else:
                show_critical(self, "请选择文件!")
        except Exception as e:
            loggings.error("import_subdie_file:" + str(e))

    def open_folder_file_path(self, value):
        file_path = self.set_save_folder()
        if file_path:
            if value == 1:
                self.le_spp_proj.setText(file_path)
            elif value == 2:
                self.le_map_path.setText(file_path)
            else:
                self.le_file_folder.setText(file_path)
        else:
            show_critical(self, "请选择文件保存的文件夹")

    def set_save_folder(self):
        prefix_path = QFileDialog.getExistingDirectory(self, "请选择选中的文件夹路径", "\\")
        if prefix_path:
            return prefix_path
        else:
            return None

    def continue_measure(self):
        self.dmo.clear()
        self.statusbar.showMessage("测试中....")
        self.resume_signal.emit()
        self.flush_data_timer.start(1000)
        show_info(self, "继续测试")

    def pause_measure(self):
        self.pause_signal.emit()
        self.flush_data_timer.stop()
        self.statusbar.showMessage("暂停测试")
        show_info(self, "暂停测试")

    def stop_measure(self):
        # self.thread_resume()
        self.measure_task.cancel()
        self.probe_task.cancel()
        # self.stop_signal.emit()
        self.flush_data_timer.stop()
        # self.pool.clear()
        self.set_start_pause_continue_stop_status(False)
        self.statusbar.showMessage("测试停止")
        show_info(self, "停止测试")

    def thread_resume(self):
        if self.probe_thread:
            self.probe_thread.resume()
        if self.measure_thread:
            self.measure_thread.resume()

    def insert_wafer(self):
        try:
            if all([self.waferId, self.machine_Id, self.batch_number, self.wafer_die_count, self.wafer_subdie_count]):
                insert_wafer(self.waferId, self.wafer_die_count, self.wafer_subdie_count, self.machine_Id, self.batch_number,
                      self.temperature, self.humidity)
            else:
                show_critical(self, "waferid,machine_Id,vendor,map_subdie等不能为空！")
        except Exception as e:
            loggings.error("insert_wafer" + str(e))

    def start_measure(self):
        try:
            self.dmo.clear()
            self.save_folder = self.le_file_folder.text()
            if self.save_folder:
                self.var_manage.save_folder = self.save_folder
                self.get_other_setting_info()
                # self.init_probe_status()
            else:
                show_critical(self, "请选择文件保存路径！")
                return
            if self.dict_setting and len(self.dict_setting):
                if self.probe_order_list:
                    test_seq = self.get_test_area_info()
                    print("test",test_seq)
                    self.probe_control2(test_seq, list(self.subdie_list), self.probe_order_list)
                    self.measure_control2(test_seq)
                    self.flush_data()
                    self.set_start_pause_continue_stop_status(True)
                    if self.result_window is None:
                        self.result_window = ResultAction()
                    self.result_window.show()
                    self.windows.append(self.result_window)
                    self.statupbar.show()
                    self.statusbar.showMessage('测试中...')
                else:
                    show_critical(self, "请先设置探针的移动顺序")
            else:
                show_critical(self, "请先设置测试条件")

        except Exception as e:
            if "510" in str(e):
                loggings.error("SiphTools软件不是Slave模式")
                show_critical(self, "请检查SiphTools软件是否为Slave模式！")
            else:
                if self.probe_task:
                    self.probe_task.separation_probes_and_chuck()
                loggings.error("start_measure_error " + str(e))
                show_critical(self, str(e))

    def measure_control(self, test_seq):
        self.measure_thread = MeasureThread(copy.deepcopy(test_seq))
        self.stop_signal.connect(self.measure_thread.cancel_thread)
        self.pause_signal.connect(self.measure_thread.pause_thread)
        self.measure_thread.signal.move_signal.connect(self.go_to_next)
        self.measure_thread.signal.test_result.connect(self.show_test_result)
        self.measure_thread.signal.errorSignal.connect(self.show_error)
        self.measure_thread.signal.completeSignal.connect(self.test_complete)
        self.measure_thread.signal.test_end.connect(self.test_finished)
        # self.measure_thread.setAutoDelete(True)
        self.pool.start(self.measure_thread)

    def test_finished(self):
        pass

    def measure_control2(self, test_seq):
        self.measure_thread = QThread()
        self.measure_task = MeasureTask(copy.deepcopy(test_seq))
        self.measure_task.moveToThread(self.measure_thread)
        self.stop_signal.connect(self.measure_task.cancel)
        self.pause_signal.connect(self.measure_task.pause)
        self.measure_task.move_signal.connect(self.go_to_next)
        self.measure_task.test_result.connect(self.show_test_result)
        self.measure_task.errorSignal.connect(self.show_error)
        self.measure_task.completeSignal.connect(self.test_complete)
        self.measure_task.test_end.connect(self.measure_thread.quit)
        self.measure_task.test_end.connect(self.measure_task.deleteLater)
        self.measure_thread.finished.connect(self.measure_thread.deleteLater)
        self.measure_thread.finished.connect(self.measure_thread.wait)
        self.measure_thread.start()
        self.thread_pool.append(self.measure_thread)

    def probe_control2(self, test_seq, subdie_list, probe_order_list):
        self.probe_thread = QThread()
        self.probe_task = ProbeStationTask("192.168.1.100", copy.deepcopy(test_seq), subdie_list, probe_order_list)
        self.probe_task.moveToThread(self.probe_thread)
        self.probe_thread.started.connect(self.probe_task.run)
        self.probe_task.wafer_result_signal.connect(self.init_probe)
        self.probe_task.TestSignal.connect(self.measurement)
        self.probe_task.send_map.connect(self.show_die_map_info)
        self.probe_task.err_signal.connect(self.show_probe_err)
        self.probe_task.End.connect(self.test_complete)
        self.probe_task.End.connect(self.probe_thread.quit)
        self.probe_task.End.connect(self.probe_task.deleteLater)
        self.probe_thread.finished.connect(self.probe_thread.deleteLater)
        self.probe_thread.finished.connect(self.probe_thread.wait)
        self.probe_thread.start()
        self.thread_pool.append(self.probe_thread)

    def probe_control(self, test_seq, subdie_list, probe_order_list):
        self.probe_thread = ProbeStationThread("192.168.1.100", copy.deepcopy(test_seq), subdie_list, probe_order_list)
        self.stop_signal.connect(self.probe_thread.cancel_thread)
        self.pause_signal.connect(self.probe_thread.pause_thread)
        self.resume_signal.connect(self.probe_thread.resume_thread)
        self.probe_thread.sign.wafer_result_signal.connect(self.init_probe)
        self.probe_thread.sign.TestSignal.connect(self.measurement)
        self.probe_thread.sign.send_map.connect(self.show_die_map_info)
        self.probe_thread.sign.err_signal.connect(self.show_probe_err)
        self.probe_thread.sign.End.connect(self.test_complete)
        # self.probe_thread.setAutoDelete(True)
        self.pool.start(self.probe_thread)

    def test_complete(self, value):
        if value:
            self.thread_resume()
            self.stop_signal.emit()
            self.set_start_pause_continue_stop_status(False)
            self.statusbar.showMessage('测试完成,探针和chuck分离！')
            show_info(self, "测试完成,探针和chuck分离！")
            self.pool.clear()

    def show_error(self, error_msg, error_description):
        loggings.error("error" + str(error_description))
        self.thread_resume()
        self.stop_signal.emit()
        self.statusbar.showMessage('测试异常,测试停止')
        show_critical(self, str(error_description))

    def show_probe_err(self, value):
        self.thread_resume()
        self.stop_signal.emit()
        self.set_start_pause_continue_stop_status(False)
        self.statusbar.showMessage('测试异常,测试停止')
        show_critical(self, str(value))
        show_critical(self, "探针台连接异常,探针和chuck分离！")

    def show_die_map_info(self, value):
        if value:
            self.statupbar.setValue(int(float(int(value.CurDie) / int(value.DiesCount)) * 100))
            self.die_x = value.DieX
            self.die_y = value.DieY
            self.subdie_number = value.CurSite
            self.shot_number = value.CurDie
            self.var_manage.diex = self.die_x
            self.var_manage.diey = self.die_y
            self.var_manage.subdie_name = value.SubdieLabel
            self.var_manage.subdie_number = self.subdie_number
            self.var_manage.shot_number = self.shot_number

    def measurement(self, value):
        if value:
            self.measure_thread.resume()

    def go_to_next(self, value):
        if value:
            self.probe_thread.resume()

    def show_test_result(self, value):
        if value == 2:
            show_info(self, "暂停测试，点击按钮后继续测试")
            self.measure_thread.resume()

    def get_other_setting_info(self):
        other_info_pair = namedtuple("other_info", "humidity,temperature,waveguide_length,unit_Loss")
        self.humidity = self.le_humidity.text()
        self.temperature = self.le_temperature.text()
        waveguide_length = self.le_waveguide_length.text()
        unit_loss = self.le_loss_unit.text()
        return other_info_pair(self.humidity, self.temperature, waveguide_length, unit_loss)

    def get_test_area_info(self):
        try:
            listwidgetcount = self.lw_test_area.count()
            test_para = []
            test_item = namedtuple("test_item", "subdie,test_pro,test_set")
            for i in range(0, listwidgetcount):
                items = self.lw_test_area.itemWidget(self.lw_test_area.item(i)).findChildren(QWidget)
                # test_para.append(test_item(items[0].currentText(), items[8].currentText(), self.setting_info[i - 1]))
                para = self.dict_setting[
                    f"{i}_{items[0].currentText()}_{items[8].currentText()}"] if f"{i}_{items[0].currentText()}_{items[8].currentText()}" in self.dict_setting else None
                if para:
                    test_para.append(test_item(items[0].currentText(), items[8].currentText(), para))
            return test_para
        except Exception as e:
            loggings.error("get_test_area_info" + str(e))

    def get_oo_setting(self):
        oo_setting = namedtuple("oo_setting", "power,wave,range")
        le_oo_power = self.le_oo_8164_power.text()
        le_oo_wave = self.le_oo_8164_wave.text()
        cbb_oo_7744_range = self.cb_oo_7744_range.currentText()
        return oo_setting(le_oo_power, le_oo_wave, cbb_oo_7744_range)

    def get_oe_setting(self):
        oe_setting = namedtuple("oe_setting", "power,wave,voltage,range,nplc")
        le_oe_power = self.le_oe_8164_power.text()
        le_oe_wave = self.le_oe_8164_wave.text()
        le_oe_bias_voltage = self.le_oe_source_offset.text()
        cbb_range = self.cb_oe_source_range.currentText()
        cbb_nplc = self.cb_oe_source_speed.currentText()
        return oe_setting(le_oe_power, le_oe_wave, le_oe_bias_voltage, cbb_range, cbb_nplc)

    def get_loss_setting(self):
        loss_setting = namedtuple("loss_setting", "loss_gc_type,power_value,start_wave,stop_wave,step_wave,power_range")
        loss_gc_type = self.bg_group.checkedId()
        le_lc_power = self.le_lc_8164_power.text()
        le_lc_step = self.le_lc_8164_step.text()
        le_lc_start_wave = self.le_lc_8164_wave_start.text()
        le_lc_stop_wave = self.le_lc_8164_wave_stop.text()
        cb_lc_7744_range = self.cb_lc_7744_range.currentText()
        if all([loss_gc_type, le_lc_power, le_lc_step, le_lc_start_wave, le_lc_stop_wave, cb_lc_7744_range]):
            return loss_setting(loss_gc_type, le_lc_power, le_lc_start_wave, le_lc_stop_wave, le_lc_step,
                                cb_lc_7744_range)

    # 创建第一行表头  ListWidget 的第一行
    @staticmethod
    def create_title():
        wd_label = QWidget()
        wd_label.setFixedWidth(600)
        hl_sub_die = QHBoxLayout(wd_label)
        hl_sub_die.setContentsMargins(10, 0, 20, 0)
        hl_sub_die.setSpacing(40)
        lb_sub_die_id = QLabel("SubDie ID".ljust(20))
        lb_test_proj = QLabel("测试工程")
        lb_proj_setup = QLabel("工程Setup")
        hl_sub_die.addWidget(lb_sub_die_id)
        hl_sub_die.addWidget(lb_test_proj)
        hl_sub_die.addWidget(lb_proj_setup)
        return wd_label

    # 创建每个 SubDie 行，是在 ListWidget 中的每一行
    def create_widget(self, subdie_list, item_row):
        for sub_die in subdie_list:
            for pro_id in self.project_list:
                self.dialog_manager_dict[f"row{item_row}".lower()] = None
                # self.dialog_manager_dict[f"row{item_row}_{sub_die}_{pro_id}".lower()] = None
        wd_sub_die = QWidget()
        wd_sub_die.setFixedWidth(600)
        hl_sub_die = QHBoxLayout(wd_sub_die)
        hl_sub_die.setContentsMargins(10, 0, 20, 0)
        hl_sub_die.setSpacing(50)
        cb_seq_name = QComboBox()
        cb_seq_name.setObjectName(f"cb_seq_name{item_row}")
        if subdie_list:
            cb_seq_name.addItems(subdie_list)
        # 测试工程
        cb_test_project = QComboBox()
        cb_test_project.setObjectName(f"cb_test_project{item_row}")
        cb_test_project.addItems(self.project_list)
        # 工程 Setup
        pb_proj_setup = QPushButton("SetUp")
        pb_proj_setup.clicked.connect(self._show_test_mode_wd)
        hl_sub_die.addWidget(cb_seq_name)
        hl_sub_die.addWidget(cb_test_project)
        hl_sub_die.addWidget(pb_proj_setup)
        return wd_sub_die

    def add_list_item(self):
        if self.subdie_list:
            row = self.lw_test_area.count()
            self.add_newWidget(row, self.subdie_list)
        else:
            show_critical(self, "请先导入Subdie文件！")

    def delete_list_item(self):
        row = self.lw_test_area.count()
        if row:
            self.lw_test_area.takeItem(row - 1)
            for key in list(self.dialog_manager_dict.keys()):
                if f"row{row - 1}" in key:
                    del (self.dialog_manager_dict[key])
        else:
            show_critical(self, "没有需要删除的数据")

    # 按列表生成全部 Sub Die
    def generate_test_items(self, subdie_list, listwidget):
        # 删除listWidget中的itemWidget，直接使用clear()方法不能够完全删除itemWidget，需要使用这种方式才能完全删除。。。。。。。
        for item in self.item_list:
            self.lw_test_area.removeItemWidget(item)
        self.item_list.clear()
        self.lw_test_area.clear()
        self.dialog_manager_dict = {}
        # 生成的控件添加到每一行
        item_row = 0
        for _ in range(len(listwidget)):
            self.add_newWidget(item_row, subdie_list)
            item_row += 1

    def add_newWidget(self, item_row, subdie_list):
        new_widget = self.create_widget(subdie_list, item_row)
        self.add_listWidget_item(new_widget)

    def add_listWidget_item(self, item_content):
        list_item = QListWidgetItem()
        list_item.setSizeHint(QSize(50, 40))
        self.item_list.append(list_item)
        self.lw_test_area.addItem(list_item)
        self.lw_test_area.setItemWidget(list_item, item_content)

    # 生成一个所有控件的列表，根据 index 返回具体索引控件
    def show_setting_info(self, prefix_title, value):
        self.dict_setting[prefix_title] = value
        self.lw_test_area.item(value.row_num).setCheckState(Qt.Checked)

    # TEST MODE 对应界面动态框架
    def _show_test_mode_wd(self):
        sender = self.sender()
        sub_die_name = sender.parent().children()[1].currentText().strip()  # 名称
        self.test_mode_index = sender.parent().children()[2].currentIndex()  # 模式索引
        test_mode_name = sender.parent().children()[2].currentText()  # 模式名称
        print(sub_die_name,self.test_mode_index,test_mode_name)
        # 界面构建 在这里接收一个对应的 QWidget 控件，加到自己的框中  current_sub_die_info 中保存有相应信息
        if self.dcp_list:
            buttonpos = sender.mapToGlobal(QPoint(0, 0)) - self.lw_test_area.mapToGlobal(QPoint(0, 0))
            # 获取到对象
            item_row = self.lw_test_area.indexAt(buttonpos).row()
            self.init_setup_dialog(item_row, sub_die_name, test_mode_name)
        else:
            show_critical(self, "请先设置DCP和SMU的对应关系!")

    def init_setup_dialog(self, item_row, sub_die_name, test_mode_name):
        # dict_key = f"row{item_row}_{sub_die_name}_{test_mode_name}"
        dict_key = f"row{item_row}"
        # if self.dialog_manager_dict[dict_key.lower()] is None:
        self.dialog_manager_dict[dict_key.lower()] = CommonDialog(
            item_row,
            sub_die_name,
            test_mode_name,
            self.dcp_list,
            dict_key)
        self.dialog_manager_dict[
            dict_key.lower()].send_setting_info.connect(
            self.show_setting_info)
        self.dialog_manager_dict[dict_key.lower()].exec()

    # DCP 对应界面
    def _show_dcp_wd(self):
        self.dia_dcp.exec()

    # 更新 DCP 探针设置
    def update_dcp_info(self, dcp_smu_pair):
        smu_device_dict = read_ini_info_2_dict()
        new_cb_index_list = []
        for cb in self.dia_dcp.children()[1].children()[2:-1:2]:
            new_cb_index_list.append(cb.currentIndex())
        self.dcp_list = {}
        for dcp, smu in dcp_smu_pair.items():
            if smu != "None" and smu != "GND":
                self.dcp_list[f"{dcp}:{smu}"] = smu_device_dict[smu.lower()]
        if self.check_dcp_valid(new_cb_index_list):
            save_data2_2_ini(constant.GUI_SETTING, "pair_smu_dcp", dcp_smu_pair)
            show_info(self, "应用成功！")
        else:
            show_critical(self, "SMU 设置不合法，请重试！")

    def read_config(self):  # 读取配置文件
        file_path, _ = QFileDialog.getOpenFileName(self, "导入文件", "./", "HSP Files (*.hsp)")
        if file_path:  # 实际hsp文件
            if pathlib.Path(file_path).exists():
                self.config = configparser.ConfigParser()
                self.config.read(file_path)
                self._recall_hsp(self.config)
            else:
                show_critical(self, "请导入正确的文件!")
        else:
            show_critical(self, "请选择文件!")

    def _recall_hsp(self, config_obj):
        self.dia_dcp._common_recall_hsp(self.dia_dcp, config_obj)
        self._common_recall_hsp(self, config_obj)
        dialog_list = config_obj.options("dialog_init")  # recall dialog settings
        for dialog_name in dialog_list:
            init_list = json.loads(self.config.get("dialog_init", dialog_name))
            self.dialog_manager_dict[dialog_name] = CommonDialog(*init_list)
            self.dialog_manager_dict[dialog_name].send_setting_info.connect(self.show_setting_info)
            self.dialog_manager_dict[dialog_name]._recall_hsp(config_obj)

    def _common_recall_hsp(self, parent_widget, config_obj):
        auto_save_dict = {f"main_settings/cb": QComboBox, f"main_settings/le": QLineEdit, "main_settings/de": QDateEdit}
        self.result_dict = json.loads(config_obj.get("main_init", "subdie_dict").strip())  # subdie dict
        self.project_list = json.loads(config_obj.get("main_init", "project_list").strip())  # subdie dict
        self.dcp_list = json.loads(config_obj.get("main_init", "dcp_list").strip())  # subdie dict
        self.subdie_list = self.result_dict.keys()
        dialog_list = config_obj.options("dialog_init")
        self.generate_test_items(self.subdie_list, dialog_list)  # 构造subdie pro display

        for section_name in auto_save_dict:  # recall subdie display settings
            if section_name in config_obj.sections():
                for option_name in config_obj.options(section_name):
                    tool_obj = parent_widget.findChild(auto_save_dict[section_name], option_name)
                    if tool_obj:
                        if section_name[-2:] == "rb":
                            tool_obj.setChecked(json.loads(config_obj.get(section_name, option_name)))
                        elif section_name[-2:] == "le":
                            tool_obj.setText(config_obj.get(section_name, option_name))
                        elif section_name[-2:] == "sb":
                            tool_obj.setValue(json.loads(config_obj.get(section_name, option_name)))
                        elif section_name[-2:] == "cb":
                            tool_obj.setCurrentIndex(int(json.loads(config_obj.get(section_name, option_name))))
                        elif section_name[-2:] == "ck":
                            tool_obj.setChecked(json.loads(config_obj.get(section_name, option_name)))
                        elif section_name[-2:] == "sw":
                            tool_obj.setState(json.loads(config_obj.get(section_name, option_name)))
                        elif section_name[-2:] == "de":
                            date_list = config_obj.get(section_name, option_name).split("-")
                            tool_obj.setDate(QDate(int(date_list[0]),
                                                   int(date_list[1]),
                                                   int(date_list[2])))

    def save_file(self):
        if self.result_dict and self.project_list and self.dcp_list:
            file_path, ok = QFileDialog.getSaveFileName(self, "工程文件存储位置", "./",
                                                        "HSP Files (*.hsp)")
            if file_path:  # 实际hsp文件
                self._save_hsp(file_path)
        else:
            show_critical(self, "设置不合法，请重试！")

    def _save_hsp(self, file_path):
        auto_save_dict = {"rb": QRadioButton, "le": QLineEdit,
                          "cb": QComboBox, "ck": QCheckBox, "de": QDateEdit}
        self.config = configparser.ConfigParser()
        self.config.add_section(f"main_init")
        self.config.set("main_init", "subdie_dict", json.dumps(self.result_dict))  # subdie dict
        self.config.set("main_init", "project_list", json.dumps(self.project_list))  # subdie dict
        self.config.set("main_init", "dcp_list", json.dumps(self.dcp_list))  # subdie dict
        self._common_save_hsp(self, self.config, "main_settings", auto_save_dict)
        for pro_dialog_name in self.dialog_manager_dict:  # 遍历每个dialog
            dialog_obj = self.dialog_manager_dict[pro_dialog_name]
            if dialog_obj:  # 判断窗口是否存在
                dialog_obj._save_hsp(self.config, auto_save_dict)  #
        self.dia_dcp._common_save_hsp(self.dia_dcp, self.config, "dcp_settings", auto_save_dict)  # dcp dialog save

        with open(file_path, "w", encoding="utf-8") as fp:
            self.config.write(fp)

    def _common_save_hsp(self, parent_widget, config_obj, section_head, auto_save_dict):
        global raw_value
        for tool_head_name in auto_save_dict:
            child_list = parent_widget.findChildren(auto_save_dict[tool_head_name])
            current_section = f"{section_head}/{tool_head_name}"
            config_obj.add_section(current_section)
            for tool_obj in child_list:
                id_name = tool_obj.objectName()
                if "qt_spinbox_lineedit" in id_name or "qt_calendar_yearedit" in id_name or "lbt_title" in id_name or id_name == "":
                    continue
                tool_obj = parent_widget.findChild(auto_save_dict[tool_head_name], id_name)
                if tool_head_name == "rb":
                    raw_value = tool_obj.isChecked()
                elif tool_head_name == "le":
                    raw_value = tool_obj.text()
                elif tool_head_name == "sb":
                    raw_value = tool_obj.value()
                elif tool_head_name == "cb":
                    raw_value = tool_obj.currentIndex()
                elif tool_head_name == "ck":
                    raw_value = tool_obj.isChecked()
                elif tool_head_name == "sw":
                    raw_value = tool_obj.getState()
                elif tool_head_name == "de":
                    raw_value = str(tool_obj.date().toPyDate())
                config_obj.set(current_section, id_name,
                               raw_value if isinstance(raw_value, str) else json.dumps(raw_value))

    # 判断 DCP 定义合法性
    @staticmethod
    def check_dcp_valid(dcp_list):
        new_list = [i for i in dcp_list if i != 0]
        # 该列表中没有重复元素，说明无重复的DCP
        if len(new_list) == len(set(new_list)):
            return True
        return False

    def closeEvent(self, event):
        result = QMessageBox.question(
            self,
            "Confirm Exit...",
            "<font size='3'>是否退出软件?</font>",
            QMessageBox.Yes | QMessageBox.No)
        if result == QMessageBox.Yes:
            self.close_all_window()
            event.accept()
        else:
            event.ignore()

    def close_all_window(self):
        if self.windows:
            for window in self.windows:
                window.close()
        self.windows.clear()

    def flush_data_task(self):
        if self.dmo and self.dmo.data and self.save_folder:
            image_path = f'{self.var_manage.save_folder}/{self.dmo.data["test"].file_name}.png'
            file_path = f'{self.var_manage.save_folder}/{self.dmo.data["test"].file_name}.csv'
            self.result_window._refresh_table_data(self.dmo.data, file_path)
            self.result_window._draw_plot(self.dmo.data["test"].test_result, self.dmo.data["test"].title,
                                          self.dmo.data["test"].x_label, self.dmo.data["test"].y_label,
                                          file_path=image_path,
                                          show_log=True if self.dmo.data["test"].curve_type == "log" else False,
                                          y_invert=self.dmo.data["test"].y_invert)
            self.dmo.clear()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Driver()
    window.show()
    sys.exit(app.exec())
